package com.gzz.orchid.common.core.util;


import com.gzz.orchid.common.core.exception.ArgumentException;

import java.time.LocalDate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据验证
 * .有关数字的正则表达式：
 * <p>
 * 数字：^[0-9]*$
 * n位的数字：^\\d{n}$
 * 至少n位的数字：^\\d{n,}$
 * m-n位的数字：^\\d{m,n}$
 * 零和非零开头的数字：^(0|[1-9][0-9]*)$
 * 非零开头的最多带两位小数的数字：^([1-9][0-9]*)+(.[0-9]{1,2})?$
 * 带1-2位小数的正数或负数：^(\\-)?\\d+(\\.\\d{1,2})?$
 * 正数、负数、和小数：^(\\-|\\+)?\\d+(\\.\\d+)?$
 * 有两位小数的正实数：^[0-9]+(.[0-9]{2})?$
 * 有0~3位小数的正实数：^[0-9]+(\\.[0-9]{1,3})?$
 * 非零的正整数：^[1-9]\\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\\+?[1-9][0-9]*$
 * 非零的负整数：^\\-[1-9][0-9]*$ 或 ^-[1-9]\\d*$
 * 非负整数：^\\d+$ 或 ^[1-9]\\d*|0$
 * 非正整数：^-[1-9]\\d*|0$ 或 ^((-\\d+)|(0+))$
 * 非负浮点数：^\\d+(\\.\\d+)?$ 或 ^[1-9]\\d*\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0$
 * 非正浮点数：^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$ 或 ^(-([1-9]\d\*\\.\\d*|0\\.\\d*[1-9]\\d*))|0?\\.0+|0$
 * 正浮点数：^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$ 或 ^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$
 * 负浮点数：^-([1-9]\\d*\.\\d*|0\\.\\d*[1-9]\\d*)$ 或 ^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
 * 浮点数：^(-?\\d+)(\\.\\d+)?$ 或 ^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$
 * ————————————————
 * 英文和数字：^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
 * 长度为3-20的所有字符：^.{3,20}$
 * 由26个英文字母组成的字符串：^[A-Za-z]+$
 * 由26个大写英文字母组成的字符串：^[A-Z]+$
 * 由26个小写英文字母组成的字符串：^[a-z]+$
 * 由数字和26个英文字母组成的字符串：^[A-Za-z0-9]+$
 * 由数字、26个英文字母或者下划线组成的字符串：^\\w+$ 或 ^\\w{3,20}$
 * 中文、英文、数字包括下划线：^[\\u4E00-\\u9FA5A-Za-z0-9_]+$
 * 中文、英文、数字但不包括下划线等符号：^[\\u4E00-\\u9FA5A-Za-z0-9]+$ 或 ^[\\u4E00-\\u9FA5A-Za-z0-9]{2,20}$
 * 可以输入含有^%&',;=?$\"等字符：[^%&',;=?$\\x22]+
 * 禁止输入含有~的字符：[^~\\x22]+
 * 中国邮政编码：[1-9]\d{5}(?!\d)    (中国邮政编码为6位数字)
 * 中文字符的正则表达式：[\u4e00-\u9fa5]
 * ————————————————
 */
public class ValidatorUtil {

    /**
     * 抛出异常
     */
    public static void validator(boolean flg, String message) {
        if (flg) {
            throw new ArgumentException(message);
        }
    }

    /**
     * 手机号码格式校验
     */
    public static Boolean isMobileNumber(String str) {
        Pattern p = null;
        Matcher m = null;
        boolean b = false;
        // String s2 = "^[1](([3|5|8][\\d])|([4][4,5,6,7,8,9])|([6][2,5,6,7])|([7][^9])|([9][1,8,9]))[\\d]{8}$";// 验证手机号
        // 验证手机号
        String s2 = "^[1][\\d]{10}$";
        if (!StringUtil.isEmpty(str)) {
            p = Pattern.compile(s2);
            m = p.matcher(str);
            b = m.matches();
        }
        return b;
    }

    /**
     * 身份证号码格式校验
     */
    public static Boolean isUid(String str) {
        if (!str.matches("\\d{17}(\\d|x|X)$")) {
            return false;
        }
        int year = LocalDate.now().getYear();
        if (Integer.parseInt(str.substring(6, 10)) < 1900 || Integer.parseInt(str.substring(6, 10)) > year) {
            // 7-10位是出生年份，范围应该在1900-当前年份之间
            return false;
        }
        // 11-12
        if (Integer.parseInt(str.substring(10, 12)) < 1 || Integer.parseInt(str.substring(10, 12)) > 12) {
            // 11-12位代表出生月份，范围应该在01-12之间
            return false;
        }
        // 13-14
        if (Integer.parseInt(str.substring(12, 14)) < 1 || Integer.parseInt(str.substring(12, 14)) > 31) {
            // 13-14位是出生日期，范围应该在01-31之间
            return false;
        }
        // 校验第18位
        // S = Sum(Ai * Wi), i = 0, ... , 16 ，先对前17位数字的权求和
        // Ai:表示第i位置上的身份证号码数字值
        // Wi:表示第i位置上的加权因子
        // Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
        String[] tempA = str.split("|");
        int[] a = new int[18];
        for (int i = 0; i < tempA.length - 2; i++) {
            a[i] = Integer.parseInt(tempA[i + 1]);
        }
        // 加权因子
        int[] w = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum = sum + a[i] * w[i];
        }
        // Y = mod(S, 11)
        // 通过模得到对应的校验码
        // Y: 0 1 2 3 4 5 6 7 8 9 10
        // 校验码: 1 0 X 9 8 7 6 5 4 3 2
        String[] v = {"1", "0", "x", "9", "8", "7", "6", "5", "4", "3", "2"};
        int y = sum % 11;
        // 第18位校验码错误
        return v[y].equalsIgnoreCase(str.substring(17));
    }
}
